package com.mybatis.jpa.SqlProvider;

import com.mybatis.jpa.meta.MetaCell;
import org.apache.ibatis.builder.annotation.ProviderContext;
import org.apache.ibatis.jdbc.SQL;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * @Author lgt
 * @Date 2017/10/10 : 21:15
 */
public class UpdateSqlProvider {


    private static final Logger logger = LoggerFactory.getLogger(UpdateSqlProvider.class);

    public String updateByPrimaryKey(Map<String, Object> paraMap,ProviderContext context) {
        Check.notNull(paraMap.get("pk"), "pk must not be null");
        Check.notNull(paraMap.get("t"), "para 2  must not be null");
        MetaCell meta = ProviderUtil.getMetaCellByMapper(context);
        SQL sql = new SQL();
        sql.UPDATE(meta.getTableName());
        for (int i = 0; i < meta.getColumns().length; i++) {
            sql.SET(meta.getColumns()[i] + " = #{t." + meta.getFields()[i] + "}");
        }
        sql.WHERE(meta.getPrimaryKey() + "= #{pk}");
        logger.debug("updateByPrimaryKey :{}", sql.toString());
        return sql.toString();
    }

    public String updateByPrimaryKeySelectivity(Map<String, Object> paraMap, ProviderContext context){
        Check.notNull(paraMap.get("pk"), "pk must not be null");
        MetaCell meta = ProviderUtil.getMetaCellByMapper(context);
        Object t = paraMap.get("t");
        Check.notNull(t, "para 2  must not be null");
        SQL sql = new SQL();
        sql.UPDATE(meta.getTableName());
        Class<?> tClass = t.getClass();
        try {
            Method[] methods = tClass.getMethods();
            Map<String, Method> methodMap = Arrays.stream(methods).filter(m->m.getName().contains("get"))
                    .collect(Collectors.toMap(m -> m.getName().replace("get", "").toLowerCase(), m -> m));
            for (int i = 0; i < meta.getFields().length; i++) {
                Method method = methodMap.get(meta.getFields()[i].toLowerCase());
                Object value = method.invoke(t);
                if (Objects.nonNull(value)) {
                    sql.SET(meta.getColumns()[i] + " = #{t." + meta.getColumns()[i] + "}");
                }
            }
        } catch (IllegalAccessException | InvocationTargetException e) {
            throw new RuntimeException(" updateByPrimaryKey:", e);
        }
        sql.WHERE(meta.getPrimaryKey() + "= #{pk}");
        logger.debug("updateByPrimaryKeySelectivity :{}", sql.toString());
        return sql.toString();
    }

    public String updateByConditionSelectivity(Condition condition, ProviderContext context) {
        Check.notNull(condition,"condition cannot be null");
        MetaCell meta = ProviderUtil.getMetaCellByMapper(context);
        Object t = condition.getUpdateLoad();
        Check.notNull(t, "condition load cannot be null");
        SQL sql = new SQL();
        sql.UPDATE(meta.getTableName());
        Class<?> tClass = t.getClass();
        try {
            Method[] methods = tClass.getMethods();
            Map<String, Method> methodMap = Arrays.stream(methods).filter(m -> m.getName().contains("get"))
                    .collect(Collectors.toMap(m -> m.getName().replace("get", "").toLowerCase(), m -> m));
            Check.isFalse(methodMap.containsKey("updateLoad"), tClass.getName() + "cannot have the property updateLoad");
            for (int i = 0; i < meta.getFields().length; i++) {
                Method method = methodMap.get(meta.getFields()[i].toLowerCase());
                Object value = method.invoke(t);
                if (Objects.nonNull(value)) {
                    sql.SET(meta.getColumns()[i] + " = #{updateLoad." + meta.getColumns()[i] + "}");
                }
            }
        } catch (IllegalAccessException | InvocationTargetException e) {
            throw new RuntimeException(" updateByPrimaryKey:", e);
        }
        logger.debug("updateByPrimaryKeySelectivity :{} {}", sql.toString(), condition.toString());
        return sql.toString() + condition.toString();
    }
}
